﻿using Codemonk.Common.Caching;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace Codemonk.Common.Emit
{
    /// <summary>
    /// 一个基于表达式树的访问器
    /// </summary>
    public class TypeAccessor : ITypeAccessor
    {
        private readonly Type targetType;
 
        public TypeAccessor(Type targetType)
        {
            this.targetType = targetType;
            ctors = new DictionaryCache<ParameterHash, Func<object[], object>>();
            funcs = new DictionaryCache<string, ICacheCollection<ParameterHash, Func<object, object[], object>>>();
        }
        private List<string> setterNames, getterNames;
        private ICacheCollection<ParameterHash, Func<object[], object>> ctors;
        private ICacheCollection<string, ICacheCollection<ParameterHash, Func<object, object[], object>>> funcs;

        private void CheckType(object obj)
        {
            var type = obj.GetType();
            if (type != targetType) throw new ArgumentException();
        }

        public object Create(params object[] parameters)
        {
            Func<object[], object> ctor;
            var parametersPh = new ParameterHash(parameters);
            if (ctors.TryGet(parametersPh, out ctor)) return ctor(parameters);
            else
            {
                var constructorInfos = targetType.GetConstructors();
                foreach (var constructorInfo in constructorInfos)
                {
                    var paramInfos = constructorInfo.GetParameters();
                    var length = paramInfos.Length;
                    Type[] types = new Type[length];
                    for (int i = 0; i < length; i++) types[i] = paramInfos[i].ParameterType;
                    var createdctor = ExpressionHelper.CreateCtor(constructorInfo);
                    var ph = new ParameterHash(types);
                    if (ph == parametersPh) ctor = createdctor;
                    ctors.Set(ph, createdctor);
                }
            }
            if (ctor != null)
                return ctor(parameters);
            else
                throw new ArgumentException("函数没有这组参数重载的构造函数");
        }

        public void SetProperty(object target, string name, object value)
        {
            ExecuteFunc(target, string.Concat("set_", name), new object[] { value }); ;
        }

        public object GetProperty(object target, string name)
        {
            return ExecuteFunc(target, string.Concat("get_", name), null); ;
        }

        public void ExecuteAction(object target, string name, params object[] parameters)
        {
            ExecuteFunc(target, name, parameters);
        }
        //不支持同函数名同参数个数但是参数类型不同的函数重载
        public object ExecuteFunc(object target, string name, params object[] parameters)
        {
            CheckType(target);
            return InnerExecuteFunc(target, name, parameters);
        }

        private object InnerExecuteFunc(object target, string name, object[] parameters)
        {
            ICacheCollection<ParameterHash, Func<object, object[], object>> cache;
            Func<object, object[], object> func = null;
            var parametersPh = new ParameterHash(parameters);
            if (funcs.TryGet(name, out cache))
            {
                if (cache.TryGet(parametersPh, out func))
                    return func(target, parameters);
                else
                    throw new ArgumentException("函数没有这组参数的重载");
            }
            else
            {
                var members = targetType.GetMember(name);
                if (members == null) throw new ArgumentException("当前类型没有这个名称的成员");
                List<MethodInfo> methodinfos = new List<MethodInfo>();
                foreach (var member in members)
                {
                    if (member.MemberType == MemberTypes.Method)
                    {
                        var methodinfo = (MethodInfo)member;
                        methodinfos.Add(methodinfo);
                    }
                }
                if (methodinfos == null) throw new ArgumentException("当前类型没有这个名称的成员");
                cache = new DictionaryCache<ParameterHash, Func<object, object[], object>>();
                Func<object, object[], object> createdfunc;
                foreach (var methodinfo in methodinfos)
                {
                    var paramInfos = methodinfo.GetParameters();
                    var length = paramInfos.Length;
                    Type[] types = new Type[length];
                    for (int i = 0; i < length; i++) types[i] = paramInfos[i].ParameterType;
                    createdfunc = ExpressionHelper.CreateMethod(methodinfo);
                    var ph = new ParameterHash(types);
                    if (ph == parametersPh) func = createdfunc;
                    cache.Set(ph, createdfunc);
                }
                funcs.Set(name, cache);
            }
            if (func != null)
                return func(target, parameters);
            else
                throw new ArgumentException("函数没有这组参数的重载");
        }



        public IEnumerable<string> SetterNames
        {
            get
            {
                if (setterNames == null)
                {
                    lock (this)
                    {
                        if (setterNames == null)
                        {
                            setterNames = new List<string>();
                            var properties = targetType.GetProperties();
                            foreach (var property in properties)
                            {
                                if (property.CanWrite) setterNames.Add(property.Name);
                            }
                        }
                    }
                }
                foreach (var item in setterNames) yield return item;
            }
        }

        public IEnumerable<string> GetterNames
        {
            get
            {
                if (getterNames == null)
                {
                    lock (this)
                    {
                        if (getterNames == null)
                        {
                            getterNames = new List<string>();
                            var properties = targetType.GetProperties();
                            foreach (var property in properties)
                                if (property.CanRead) setterNames.Add(property.Name);
                        }
                    }
                }
                foreach (var item in getterNames) yield return item;
            }
        }
    }

    public class TypeAccessor<T>
    {
        private readonly TypeAccessor accessor;
        public TypeAccessor() { accessor = new TypeAccessor(typeof(T)); }

        public T Create(params object[] parameters)
        {
            return (T)accessor.Create(parameters);
        }

        public void SetProperty(T target, string name, object value)
        {
            accessor.SetProperty(target, name, value);
        }

        public object GetProperty(T target, string name)
        {
            return accessor.GetProperty(target, name);
        }

        public void ExecuteAction(T target, string name, params object[] parameters)
        {
            accessor.ExecuteAction(target, name, parameters);
        }

        public object ExecuteFunc(T target, string name, params object[] parameters)
        {
            return accessor.ExecuteFunc(target, name, parameters);
        }

        public IEnumerable<string> SetterNames { get { return accessor.SetterNames; } }
        public IEnumerable<string> GetterNames { get { return accessor.GetterNames; } }
    }
}
